class Timing {
    constructor({duration, iterations = 1} = {}) {
        this.startTime = Date.now()
        this.duration = duration
        this.iterations = iterations
    }

    get time() {
        return Date.now() - this.startTime
    }

    get isFinished() {
        return this.time / this.duration >= this.iterations
    }

    get p() {
        const progress = Math.min(this.time / this.duration, this.iterations)
        return this.isFinished ? 1 : progress % 1
    }

}

class Animator {
    constructor({duration, iterations}) {
        this.timing = { duration, iterations }
    }
    animate(target, update) {
        let frameIndex = 0
        const timing = new Timing(this.timing)

        return new Promise((resolve) => {
            function next() {
                if (update({target, frameIndex, timing}) != false && !timing.isFinished) {
                    requestAnimationFrame(next)
                } else {
                    resolve(timing)
                }
                frameIndex ++
            }
            next()
        })
    }
}